package cn.itcast.hiss.process.activiti.handler.processinstance;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.io.IoUtil;
import cn.hutool.core.util.StrUtil;
import cn.itcast.hiss.api.client.HissProcessConstants;
import cn.itcast.hiss.api.client.processdefinition.ProcessViewerModel;
import cn.itcast.hiss.api.client.processdefinition.viewer.*;
import cn.itcast.hiss.cmd.handler.CmdHandler;
import cn.itcast.hiss.common.enums.ActiveNodeTypeEnum;
import cn.itcast.hiss.common.enums.ModelTypeEnum;
import cn.itcast.hiss.common.enums.ProcessNodeStatusEnum;
import cn.itcast.hiss.common.enums.ProcessStatusEnum;
import cn.itcast.hiss.handler.HandlerIdClientEnum;
import cn.itcast.hiss.message.Message;
import cn.itcast.hiss.message.MessageContext;
import cn.itcast.hiss.message.sender.processdefinition.ProcessViewerModelMessage;
import cn.itcast.hiss.process.activiti.mapper.HissProcessFormMapper;
import cn.itcast.hiss.process.activiti.mapper.HissSystemUserMapper;
import cn.itcast.hiss.process.activiti.pojo.HissProcessForm;
import cn.itcast.hiss.process.activiti.pojo.HissSystemUser;
import cn.itcast.hiss.process.activiti.service.CommentService;
import org.activiti.bpmn.constants.BpmnXMLConstants;
import org.activiti.bpmn.model.Process;
import org.activiti.bpmn.model.*;
import org.activiti.engine.HistoryService;
import org.activiti.engine.RepositoryService;
import org.activiti.engine.RuntimeService;
import org.activiti.engine.TaskService;
import org.activiti.engine.history.HistoricActivityInstance;
import org.activiti.engine.history.HistoricProcessInstance;
import org.activiti.engine.history.HistoricVariableInstance;
import org.activiti.engine.impl.bpmn.behavior.ParallelMultiInstanceBehavior;
import org.activiti.engine.impl.bpmn.behavior.SequentialMultiInstanceBehavior;
import org.activiti.engine.impl.bpmn.behavior.UserTaskActivityBehavior;
import org.activiti.engine.task.IdentityLink;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.io.InputStream;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

@Component
public class PI_GetInstanceViewerHandler implements CmdHandler<ProcessViewerModelMessage> {

    @Autowired
    RuntimeService runtimeService;

    // 查询历史中的流程信息
    @Autowired
    HistoryService historyService;


    @Autowired
    RepositoryService repositoryService;

    @Autowired
    CommentService commentService;

    @Autowired
    TaskService taskService;

    @Autowired
    HissSystemUserMapper hissSystemUserMapper;
    @Autowired
    HissProcessFormMapper hissProcessFormMapper;

    @Override
    public void invoke(Message params, MessageContext messageContext) {
        // 查询出xml返回给前端：
        ProcessViewerModel processViewerModel = (ProcessViewerModel) params.getPalyload();
        String processInstanceId = processViewerModel.getProcessInstanceId();
//        ProcessInstance processInstance = runtimeService.createProcessInstanceQuery().processInstanceId(processInstanceId).singleResult();
        HistoricProcessInstance historicProcessInstance = historyService.createHistoricProcessInstanceQuery().processInstanceId(processInstanceId).singleResult();
        loadXml(historicProcessInstance,processViewerModel);//查询xml源流程图 //查询xml源流程图

        // 查询流程变量—
        List<HistoricVariableInstance> variableInstances = historyService.createHistoricVariableInstanceQuery().processInstanceId(historicProcessInstance.getId()).list();
        Map<String,Object> variables = new HashMap<>();
        for(HistoricVariableInstance variableInstance : variableInstances){
            variables.put(variableInstance.getVariableName(),variableInstance.getValue());
        }

        processInfo(variables,historicProcessInstance,processViewerModel);// 加载基本信息
        // 解析是否为管理员，既Hiss流程中心的用户，都是超管，用来管理流程的
        HissSystemUser hissSystemUser = hissSystemUserMapper.selectById(params.getMessageAuth().getCurrentUser().getUserId());
        if(hissSystemUser!=null){
            processViewerModel.getProcessInfo().setAdmin(true);
        }

        parseHighLightedData(variables,historicProcessInstance,processViewerModel);// 查询高亮活动的信息

        setProcessForm(historicProcessInstance,processViewerModel);// 加载绑定的表单数据

        // 写回数据
        messageContext.addResultAndCount("viewer",processViewerModel);
    }

    private void setProcessForm(HistoricProcessInstance processInstance,ProcessViewerModel processViewerModel){
        //  设置confgJson
        String deploymentId = processInstance.getDeploymentId();// 部署ID
        List<String> deploymentResourceNames = repositoryService.getDeploymentResourceNames(deploymentId);
        if(deploymentResourceNames.contains(HissProcessConstants.PROCESS_SHOW_CONFIG_JSON)) {
            InputStream resourceAsStream = repositoryService.getResourceAsStream(deploymentId, HissProcessConstants.PROCESS_SHOW_CONFIG_JSON);
            if (resourceAsStream != null) {
                String json = IoUtil.readUtf8(resourceAsStream);
                processViewerModel.setConfigJson(json);
            }
        }
        // 设置form
        List<HissProcessForm> hissProcessForms = hissProcessFormMapper.listFormByProcessInstanceId(processInstance.getId());
        if(hissProcessForms!=null) {
            List<ProcessFormInfo> forms = hissProcessForms.stream().map((item) -> BeanUtil.copyProperties(item, ProcessFormInfo.class)).collect(Collectors.toList());
            processViewerModel.setForms(forms);
        }
    }

    /**
     * 加载基本信息
     * @param variables 流程变量
     * @param historicProcessInstance
     * @param processViewerModel
     */
    private void processInfo(Map<String,Object> variables,HistoricProcessInstance historicProcessInstance, ProcessViewerModel processViewerModel) {
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        ProcessInfo processInfo = new ProcessInfo();
        // 设置值
        processInfo.setName(historicProcessInstance.getName());
        processInfo.setStartTime(simpleDateFormat.format(historicProcessInstance.getStartTime()));
        processInfo.setStartUserId(historicProcessInstance.getStartUserId());
        long duration = 0;// 耗时的毫秒数
        if(historicProcessInstance.getEndTime()!=null){
            processInfo.setEndTime(simpleDateFormat.format(historicProcessInstance.getEndTime()));
            duration = historicProcessInstance.getEndTime().getTime() - historicProcessInstance.getStartTime().getTime();
            processInfo.setStatus(ProcessStatusEnum.COMPLETE);
        }else{
            duration = System.currentTimeMillis() - historicProcessInstance.getStartTime().getTime();
            processInfo.setStatus(ProcessStatusEnum.ACTIVE);
        }
        processInfo.setEndReason(historicProcessInstance.getDeleteReason());
        processInfo.setDuration(formatDuration(duration));
        processInfo.setAdmin(false);
        processInfo.setType(ModelTypeEnum.DEV);
        processInfo.setTenantId(historicProcessInstance.getTenantId());
        processInfo.setParentProcessId("");
        // TODO 剩余PREPARE("预备中"),PENDING("挂起中"),在后续进行补充
        // 补充取消、不同意
        if(variables.containsKey(HissProcessConstants.PROCESS_STATUS)){
            String status = (String) variables.get(HissProcessConstants.PROCESS_STATUS);
            if(ProcessStatusEnum.CANCEL.name().equals(status)){
                processInfo.setStatus(ProcessStatusEnum.CANCEL);
            }
            if(ProcessStatusEnum.REJECT.name().equals(status)){
                processInfo.setStatus(ProcessStatusEnum.REJECT);
            }
        }


        processViewerModel.setProcessInfo(processInfo);

    }

    private String formatDuration(long durationInMillis) {
        long days = TimeUnit.MILLISECONDS.toDays(durationInMillis);
        long hours = TimeUnit.MILLISECONDS.toHours(durationInMillis) % 24;
        long minutes = TimeUnit.MILLISECONDS.toMinutes(durationInMillis) % 60;
        long seconds = TimeUnit.MILLISECONDS.toSeconds(durationInMillis) % 60;
        long milliseconds = durationInMillis % 1000;

        StringBuilder result = new StringBuilder();
        if (days > 0) {
            result.append(days).append("天 ");
        }
        if (hours > 0) {
            result.append(hours).append("小时 ");
        }
        if (minutes > 0) {
            result.append(minutes).append("分钟 ");
        }
        if (seconds > 0) {
            result.append(seconds).append("秒 ");
        }
        if (milliseconds > 0) {
            result.append(milliseconds).append("毫秒 ");
        }

        return result.toString().trim();
    }

    private void parseHighLightedData(Map<String,Object> variables,HistoricProcessInstance historicProcessInstance, ProcessViewerModel processViewerModel) {
        // 查询出当前流程实例所有需要高亮的活动节点
        List<HistoricActivityInstance> activityInstances = historyService.createHistoricActivityInstanceQuery().processInstanceId(historicProcessInstance.getId())
                .orderByHistoricActivityInstanceStartTime()
                .desc()
                .list();

        Set<String> startedActivityIds = new HashSet<>(); // 记录已经启动了的活动ID,用于查询高亮线

        Set<String> activeActivityIds = new HashSet<>(); // 记录当前正激活的活动id,注意当前激活的节点可能有多个

        // 查询该流程示例所有的评论信息,用于显示提示信息
        List<ActHiComment> processInstanceComments = commentService.selectByProcessInstanceId(historicProcessInstance.getId());
        Map<String,ActHiComment> comments = new HashMap<>();
        for(ActHiComment comment : processInstanceComments){
            if(!comments.containsKey(comment.getTaskId())) {
                comments.put(comment.getTaskId(),comment);
            }
        }

        BpmnModel bpmnModel = repositoryService.getBpmnModel(historicProcessInstance.getProcessDefinitionId()); //  获取流程的xml解析后的对象
        Process mainProcess = bpmnModel.getMainProcess(); //

        Map<String, List<ViewerNodeInfo>> highlights = processViewerModel.getHighlights();
        for (HistoricActivityInstance activityInstance : activityInstances) {
            String activityId = activityInstance.getActivityId();
            List<ViewerNodeInfo> list = highlights.getOrDefault(activityId,new ArrayList<>());
            ViewerNodeInfo viewerNodeInfo = new ViewerNodeInfo();
            boolean isActive = false;
            if(activityInstance.getEndTime()!=null){
                viewerNodeInfo.setStatus(ProcessNodeStatusEnum.COMPLETE);

                // 把取消状态的节点也添加到，需要删除的集合中activeActivityIds
                if(StrUtil.isNotEmpty(activityInstance.getDeleteReason())){ // 判断取消状态就看有没有DeleteReason
                    activeActivityIds.add(activityId);
                }
            }else{
                isActive = true;
                viewerNodeInfo.setStatus(ProcessNodeStatusEnum.ACTIVE);
                activeActivityIds.add(activityId);
                // 把激活的节点也存储到activeTasks中
                addActiveTask(processViewerModel,variables,activityInstance,mainProcess);
            }
            viewerNodeInfo.setTaskId(activityInstance.getTaskId());
            viewerNodeInfo.setNodeName(activityInstance.getActivityName());
            viewerNodeInfo.setNodeId(activityId);
            viewerNodeInfo.setAssignee(activityInstance.getAssignee());
            viewerNodeInfo.setActivityType(activityInstance.getActivityType());


            if("userTask".equals(activityInstance.getActivityType()) && activityInstance.getEndTime()!=null) {
                GraphicNodeInfo graphicNodeInfo = new GraphicNodeInfo();
                graphicNodeInfo.setStatus(ProcessNodeStatusEnum.COMPLETE);
                graphicNodeInfo.setName(activityInstance.getActivityName());
                graphicNodeInfo.setId(activityInstance.getActivityId());
                graphicNodeInfo.setTaskId(activityInstance.getTaskId());
                graphicNodeInfo.setAssignee(activityInstance.getAssignee());
                graphicNodeInfo.setActivityType("UserTask");
                // 活动名称必须为大写，同时必须按照开始时间正序排列
                processViewerModel.getNodes().add(0,graphicNodeInfo);
            }

            // 解析悬浮提示信息
            parseTip(viewerNodeInfo,comments,activityInstance);

            // 处理多个任务时，节点状态显示不对的情况
            if(isActive){
                list.add(0,viewerNodeInfo);
            }else {
                list.add(viewerNodeInfo);
            }
            highlights.put(activityId,list);

            startedActivityIds.add(activityId);


        }

        // 解析高亮线
        parseHighLightedLine(mainProcess,processViewerModel,activityInstances,startedActivityIds);
        // 删除多余的高亮线和节点，即当前正在激活节点之后的线和节点
        deleteHighLighted(mainProcess,processViewerModel,activeActivityIds);
    }

    private void addActiveTask(ProcessViewerModel processViewerModel, Map<String, Object> variables, HistoricActivityInstance activityInstance, Process mainProcess) {
        ActiveNodeInfo activeNodeInfo = new ActiveNodeInfo();
        activeNodeInfo.setTaskId(activityInstance.getTaskId()); // 只有需要人干预的节点才有taskId
        activeNodeInfo.setAssignee(activityInstance.getAssignee());
        activeNodeInfo.setActivityType(activityInstance.getActivityType());
        activeNodeInfo.setNodeType(parseMultiInstance(activityInstance,variables,mainProcess));// 解析激活节点的类型
        activeNodeInfo.setNodeId(activityInstance.getActivityId());
        parseTaskCandidate(activeNodeInfo);// 查询候选的用户
        processViewerModel.getActiveTasks().add(activeNodeInfo);
    }

    /**
     * 查询候选的用户
     * @param activeNodeInfo
     */
    private void parseTaskCandidate(ActiveNodeInfo activeNodeInfo) {
        if(StrUtil.isNotEmpty(activeNodeInfo.getTaskId())) {
            List<IdentityLink> identityLinksForTask = taskService.getIdentityLinksForTask(activeNodeInfo.getTaskId());
            if(identityLinksForTask!=null) {
                Map<String, String> map = new HashMap<>();
                for (IdentityLink identityLink : identityLinksForTask) {
                    if(StrUtil.isNotEmpty(identityLink.getUserId())) {
                        map.put(identityLink.getUserId(), identityLink.getType());
                    }
                    if(StrUtil.isNotEmpty(identityLink.getGroupId())) {
                        map.put(identityLink.getGroupId(), identityLink.getType());
                    }
                }
                activeNodeInfo.setCandidateUser(map);
            }
        }
    }

    /**
     * 解析节点的类型
     * @param activityInstance
     * @param variables
     * @param mainProcess
     * @return
     */
    private ActiveNodeTypeEnum parseMultiInstance(HistoricActivityInstance activityInstance, Map<String, Object> variables, Process mainProcess) {
        // 1、依据xml中节点的处理来，来判断多实例的类型
        FlowNode flowNode = (FlowNode) mainProcess.getFlowElement(activityInstance.getActivityId());
        Object behavior = flowNode.getBehavior(); // 获取xml节点对应在Activiti中的处理java类
        if(behavior instanceof ParallelMultiInstanceBehavior){
            return ActiveNodeTypeEnum.MULTIPLE_PARALLEL;
        }
        if(behavior instanceof SequentialMultiInstanceBehavior){
            return ActiveNodeTypeEnum.MULTIPLE_SEQUENTIAL;
        }
        // 2、从流程变量中获取类型，来判断知会和抄送
        if(variables.containsKey(HissProcessConstants.NODE_TYPE)){
            String typeName = (String) variables.get(HissProcessConstants.NODE_TYPE);
            if(StrUtil.isNotEmpty(typeName)){
                return ActiveNodeTypeEnum.valueOf(typeName);
            }
        }
        // 3、是否是userTask，如果是则从Usertask中识别多人、开始、单人节点
        if(behavior instanceof UserTaskActivityBehavior){
            // 业务模式下，解析节点的类型
            UserTask userTask = (UserTask) flowNode;
            String nodeType = userTask.getAttributeValue(BpmnXMLConstants.ACTIVITI_EXTENSIONS_NAMESPACE, HissProcessConstants.NODE_TYPE);
            if(StrUtil.isNotEmpty(nodeType)){
                return ActiveNodeTypeEnum.valueOf(nodeType);
            }
            return ActiveNodeTypeEnum.SINGLE_APPROVE;
        }

        // 4、返回默认值
        return ActiveNodeTypeEnum.NONE;

    }

    private void parseTip(ViewerNodeInfo viewerNodeInfo, Map<String, ActHiComment> commentMap, HistoricActivityInstance activityInstance) {
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        //- 对于有人审批的节点，则使用表格，显示每个节点操作人每次的操作明细
        //- 对于除审批之外的图例，比如开始、网关、中间事件、结束等图例，没有办理人，因此返回简单的时间信息即可
        if("userTask".equals(activityInstance.getActivityType())){  // 有人审批的节点
            viewerNodeInfo.setTipTitle(activityInstance.getActivityName());
            // 人工：先评论信息
            String taskId = activityInstance.getTaskId();
            ActHiComment actHiComment = commentMap.get(taskId);
            ViewerTipInfo viewerTipInfo = new ViewerTipInfo();
            viewerTipInfo.setStartTime(simpleDateFormat.format(activityInstance.getStartTime()));
            long duration = 0;
            if(activityInstance.getEndTime()!=null){
                viewerTipInfo.setEndTime(simpleDateFormat.format(activityInstance.getEndTime()));
                duration = activityInstance.getEndTime().getTime()-activityInstance.getStartTime().getTime();
            }else{
                duration = System.currentTimeMillis()-activityInstance.getStartTime().getTime();
            }
            viewerTipInfo.setDuration(formatDuration(duration));
            // 如果有评论信息，才拼接信息
            if(actHiComment!=null) {
                viewerTipInfo.setUserId(actHiComment.getUserId());
                viewerTipInfo.setUserName(actHiComment.getUserId());
                viewerTipInfo.setOperateType(actHiComment.getMessage());
                viewerTipInfo.setOperateNote(actHiComment.getFullMsg());
            }else{
                viewerTipInfo.setUserId(activityInstance.getAssignee());
                viewerTipInfo.setUserName(activityInstance.getAssignee());
            }
            viewerTipInfo.setCacelReson(activityInstance.getDeleteReason());
            // 取消状态
            if(StrUtil.isNotEmpty(activityInstance.getDeleteReason())){
                viewerNodeInfo.setStatus(ProcessNodeStatusEnum.CANCEL);
            }
            viewerNodeInfo.setTipObject(viewerTipInfo);
        }else{
            viewerNodeInfo.setTipTitle(activityInstance.getActivityType());
            Date startTime = activityInstance.getStartTime();
            String format = simpleDateFormat.format(startTime);
            viewerNodeInfo.setTipContent(format);
        }
    }

    /**
     * 删除多余的高亮线和节点
     * @param mainProcess
     * @param processViewerModel
     * @param activeActivityIds  当前正在激活的节点
     */
    private void deleteHighLighted(Process mainProcess, ProcessViewerModel processViewerModel, Set<String> activeActivityIds) {
        Map<String, List<ViewerNodeInfo>> highlights = processViewerModel.getHighlights();
        for(String activeActivityId : activeActivityIds){
            FlowNode flowNode = (FlowNode) mainProcess.getFlowElement(activeActivityId); // 拿到节点
            deleteHighLightedNode(flowNode,highlights); // 删除flowNode之后的高亮节点和线
        }
    }

    /**
     *  删除flowNode之后的高亮节点和线
     * @param flowNode
     * @param highlights
     */
    private void deleteHighLightedNode(FlowNode flowNode, Map<String, List<ViewerNodeInfo>> highlights) {
        if(flowNode!=null){
            List<SequenceFlow> outgoingFlows = flowNode.getOutgoingFlows();  // 出度
            if(outgoingFlows!=null){
                for(SequenceFlow sequenceFlow : outgoingFlows){
                    highlights.remove(sequenceFlow.getId()); // 高亮集合中删除线
                    highlights.remove(sequenceFlow.getTargetRef());  // 高亮集合中删除线的指向节点
                    FlowNode targetFlowNode = (FlowNode) sequenceFlow.getTargetFlowElement();
                    deleteHighLightedNode(targetFlowNode,highlights); // 递归删除之后的线和节点
                }
            }
        }
    }

    /**
     * 解析高亮线
     * @param mainProcess
     * @param processViewerModel
     * @param activityInstances
     */
    private void parseHighLightedLine(Process mainProcess , ProcessViewerModel processViewerModel, List<HistoricActivityInstance> activityInstances,Set<String> startedActivityIds) {

        Map<String, List<ViewerNodeInfo>> highlights = processViewerModel.getHighlights();
        for (HistoricActivityInstance activityInstance : activityInstances) {
            FlowNode flowNode = (FlowNode)mainProcess.getFlowElement(activityInstance.getActivityId());// 查询xml中的活动节点信息
            if(flowNode!=null){
                List<SequenceFlow> outgoingFlows = flowNode.getOutgoingFlows();
                if(outgoingFlows!=null){
                    for (SequenceFlow outgoingFlow : outgoingFlows) {
                        String targetRef = outgoingFlow.getTargetRef();
                        //  判断指向节点是否在活动实例表中？
                        if(startedActivityIds.contains(targetRef)){
                            String lineId = outgoingFlow.getId();
                            ViewerNodeInfo viewerNodeInfo = new ViewerNodeInfo();
                            viewerNodeInfo.setStatus(ProcessNodeStatusEnum.COMPLETE);
                            viewerNodeInfo.setNodeId(lineId);
                            viewerNodeInfo.setActivityType("sequenceFlow");
                            highlights.put(lineId, Arrays.asList(viewerNodeInfo));
                        }
                    }
                }
            }
        }
    }

    /**
     * 查询xml源流程图
     */
    public void loadXml(HistoricProcessInstance historicProcessInstance,ProcessViewerModel processViewerModel){
        String deploymentId = historicProcessInstance.getDeploymentId();// 部署ID
        InputStream resourceAsStream = repositoryService.getResourceAsStream(deploymentId, HissProcessConstants.PROCESS_RUN_XML);
        String xml = IoUtil.readUtf8(resourceAsStream);
        processViewerModel.setContent(xml);
    }



    @Override
    public String getId() {
        return HandlerIdClientEnum.PI_GET_INSTANCE_DEV_VIEWER.getId();
    }

}
